home *** CD-ROM | disk | FTP | other *** search
/ The Games Machine 76 / XENIATGM66.iso / Indiana Jones / Indiana Jones.exe / RESOURCE / PREVIEW.GOB / cog_olv_quetzalcoatl.cog < prev    next >
Text File  |  1999-11-15  |  46KB  |  1,672 lines

  1. # Jones 3D Cog Script
  2. #
  3. # olv_quetzalcoatl.cog
  4. #
  5. # AI enhancements for Quetzalcoatl
  6. #
  7. # []
  8. #
  9. # (C) 1999 LucasArts Entertainment Company LLC. All Rights Reserved
  10. #
  11. #
  12. # ===================================================================
  13.  
  14. # ===================================================================
  15. symbols
  16.  
  17. message        startup
  18. message        created
  19. message        aievent
  20. message        timer
  21. message        damaged
  22. message        entered
  23. message        exited
  24. message        touched
  25. message        user0
  26. message        killed 
  27. message        user1        # Added by Don for cutscene.
  28.  
  29. # ************************** GLOBAL VARIABLES *************************
  30. thing        t_PatrolPt00
  31. thing        t_PatrolPt01
  32. thing        t_PatrolPt02
  33. thing        t_PatrolPt03
  34. thing        t_PatrolPt04
  35. thing        t_PatrolPt05
  36. thing        t_PatrolPt06
  37. thing        t_PatrolPt07
  38. thing        t_InMainTunnelPt
  39.  
  40. thing        t_SpikePatrolPt00
  41. thing        t_SpikePatrolPt01
  42. thing        t_SpikePatrolPt02
  43. thing        t_SpikePatrolPt03
  44. thing        t_SpikePatrolPt04        // one in front of main tunnel
  45. thing        t_OffSpikePatrolPt00
  46. thing        t_OffSpikePatrolPt01
  47. thing        t_OffSpikePatrolPt02
  48. thing        t_OffSpikePatrolPt03
  49.  
  50. thing        t_SpikeTip00
  51. thing        t_SpikeTip01
  52. thing        t_SpikeTip02
  53. thing        t_SpikeTip03
  54.  
  55. sector        s_MainTunnel
  56. sector        s_OtherTunnel
  57.  
  58. #template    tpl_spit=+quetz_spit    local
  59. #material    spit_mat=olv_4sprite_spittle.mat local
  60.  
  61. template    tpl_sparks=quetzSparksLG            local        
  62.  
  63. # ************************** SOUNDS *******************
  64.  
  65. sound        move=olv_boss_move.wav            local
  66. sound        spit=olv_boss_spit.wav            local
  67. sound        tonguehiss=olv_boss_hiss.wav    local
  68. sound        hurt1=olv_boss_hurt1.wav        local
  69. sound        hurt2=olv_boss_hurt2.wav        local
  70.  
  71. int            n_MoveSoundChannel=-1            local
  72.  
  73. keyframe    key=qu_tongue.key                local
  74.  
  75. # ************************** CONSTANTS *******************
  76. int            NUM_PATROL_POINTS=8        local
  77. int            NUM_SPIKE_POINTS=5        local
  78. int            NUM_SPIKES=4            local
  79.  
  80. int            STANDARD_MODE=0            local # under normal instinct control
  81. int            PATROL_MODE=1             local # Indy is known to be on the ground, but can't currently be seen
  82. int            SPIKE_MODE=2            local # Indy is up on a ledge
  83. int            THRASH_MODE=3            local # We've been spiked
  84.  
  85. int            HEAD_SHOT_DAMAGE=350    local 
  86. int            BODY_SHOT_DAMAGE=150    local 
  87.  
  88. flex        BASE_SEQUENTIAL_EXPLOSION_TIME=0.22 local
  89. flex        TONGUE_RELOAD=10.0        local 
  90.  
  91. int            TIMER_ID_WAKEUP_LITTLE_SNAKE=0    local
  92. int            TIMER_ID_RELOAD_LITTLE_SNAKE=1    local
  93. int            TIMER_ID_SPIT_LITTLE_SNAKE=2    local
  94. int            TIMER_ID_EXPLODE_TAIL_PIECE=3    local
  95. int            TIMER_ID_ENABLE_TONGUE=4        local
  96.  
  97. # ************************** MISC LOCAL VARS *******************
  98. cog            cog_self                local
  99.  
  100. int            n_eventType                local
  101. int            n_OldAIMode                local
  102. int            n_CrntAIMode            local
  103. int            n_RandNum                local
  104. int            n_idx                    local
  105. int            n_idx2                    local
  106. flex        f_Val1                    local
  107. flex        f_Val2                    local
  108. vector        v_Temp                    local
  109. vector        v_PosShake                local
  110. vector        v_PYRShake                local
  111. vector        indyPos                    local
  112. vector        bossPos                    local
  113. vector        v_BossLook                local
  114. vector        v_BossRight                local
  115. flex        zDiff                    local
  116. flex        f_Dist                    local
  117. flex        f_Dot                    local
  118. int            attachFlags                local
  119. int            n_SpikedIdx                local
  120. int            n_SpikeDamage            local
  121. int            b_HeadOnLaunch            local
  122. int            n_TimerID                local
  123. int            b_OKToShowTongue        local
  124. int            b_Temp                    local
  125.  
  126. int            b_AvoidCallStackDepthOverflow=0 local
  127.  
  128. thing        t_UsableSpikePatrolPt00        local
  129. thing        t_UsableSpikePatrolPt01        local    
  130. thing        t_UsableSpikePatrolPt02        local
  131. thing        t_UsableSpikePatrolPt03        local
  132. thing        t_UsableSpikePatrolPt04        local
  133.  
  134. # ************************** LITTLE SNAKE VARIABLES *************************
  135. template    tpl_LittleSnake=snake_q        local
  136.  
  137. int            b_OKToLaunchLittleSnake=1    local
  138. int            b_OKToLaunchLittleSnakeTemp    local
  139. int            n_LittleSnakesAlive=0        local
  140. int            n_LittleSnakeIdx            local
  141.  
  142. flex        f_Force                    local
  143. flex        f_Error                    local    
  144.  
  145. int            MAX_LITTLE_SNAKES=8        local 
  146. thing        t_LittleSnake00=-1                local
  147. thing        t_LittleSnake01=-1                local
  148. thing        t_LittleSnake02=-1                local
  149. thing        t_LittleSnake03=-1                local
  150. thing        t_LittleSnake04=-1                local
  151. thing        t_LittleSnake05=-1                local
  152. thing        t_LittleSnake06=-1                local
  153. thing        t_LittleSnake07=-1                local
  154.  
  155.  
  156. # ************************** INDY VARIABLES *************************
  157. thing        t_Indy                    local
  158. int            b_IndyInMainTunnel        local 
  159. int            b_IndyInOtherTunnel        local 
  160. int            b_IndyOnLedge            local
  161. int            b_IndyJOH                local
  162. int            b_OKToDamageIndy=1        local
  163.  
  164. vector        v_ToIndy                local
  165. vector        v_ToIndyFlat            local
  166. vector        v_ToFutureIndy            local
  167. vector        v_ToFutureIndyFlat        local
  168. vector        v_IndyLook                local
  169. vector        v_IndyVel                local
  170. flex        f_DistToIndy            local
  171. flex        f_DistToFutureIndy        local
  172. flex        f_FutureDelta            local
  173.  
  174. # ************************** PATROLLING VARIABLES *************************
  175. int            e_Mode                    local
  176. int            n_AtPatrolIdx            local    #return value from SetAtPatrolPt
  177. int            n_IndyNearestPatrolIdx    local     #return value from SetIndyNearestPatrolIdx
  178. int            n_PatrolPtIndexDiff        local
  179. int            n_PatrolDir                local
  180. int            n_AtSpikeIdx            local
  181. int            n_idxDest                local # waypoint index of current destination
  182. int            n_idxSpikeDest            local # waypoint index of current destination
  183. int            n_IndyNearestSpikeIdx    local     #return value from SetIndyNearestSpikeIdx
  184.  
  185. # ************************** QUETZALCOATL TEMPLATES *************************
  186. template    tpl_quetz00=quetzalcoatl    local
  187. template    tpl_quetz01=quetz01            local
  188. template    tpl_quetz02=quetz02            local
  189. template    tpl_quetz03=quetz03            local
  190. template    tpl_quetz04=quetz04            local
  191. template    tpl_quetz05=quetz05            local
  192. template    tpl_quetz06=quetz06            local
  193. template    tpl_quetz07=quetz07            local
  194. template    tpl_quetz08=quetz08            local
  195. template    tpl_quetz09=quetz09            local
  196. template    tpl_quetz10=quetz10            local
  197. template    tpl_quetz11=quetz11            local
  198. template    tpl_quetz12=quetz12            local
  199. template    tpl_quetz13=quetz13            local
  200. template    tpl_quetz14=quetz14            local
  201. template    tpl_quetz15=quetz15            local
  202. template    tpl_quetz16=quetz16            local
  203. template    tpl_quetz17=quetz17            local
  204. template    tpl_quetz18=quetz18            local
  205. template    tpl_quetz19=quetz19            local
  206. template    tpl_quetz20=quetz20            local
  207. template    tpl_quetz21=quetz21            local
  208. template    tpl_quetz22=quetz22            local
  209. template    tpl_quetz23=quetz23            local
  210. template    tpl_quetz24=quetz24            local
  211. template    tpl_quetz_tail=quetz_tail    local
  212.  
  213. template    tpl_ExplodePiece=+explode2_b    local
  214. template    tpl_ExplodeBoss=+explode4_b    local # want this to be bigger than piece explosion
  215.  
  216.  
  217. # ************************** QUETZALCOATL PIECES *************************
  218. int            MAX_SNAKE_PIECES=26                local #includes head
  219. thing        t_Boss                            local
  220. thing        t_Piece01                        local
  221. thing        t_Piece02                        local
  222. thing        t_Piece03                        local
  223. thing        t_Piece04                        local
  224. thing        t_Piece05                        local
  225. thing        t_Piece06                        local
  226. thing        t_Piece07                        local
  227. thing        t_Piece08                        local
  228. thing        t_Piece09                        local
  229. thing        t_Piece10                        local
  230. thing        t_Piece11                        local
  231. thing        t_Piece12                        local
  232. thing        t_Piece13                        local
  233. thing        t_Piece14                        local
  234. thing        t_Piece15                        local
  235. thing        t_Piece16                        local
  236. thing        t_Piece17                        local
  237. thing        t_Piece18                        local
  238. thing        t_Piece19                        local
  239. thing        t_Piece20                        local
  240. thing        t_Piece21                        local
  241. thing        t_Piece22                        local
  242. thing        t_Piece23                        local
  243. thing        t_Piece24                        local
  244. thing        t_Piece25                        local
  245.  
  246. template    qdebris0=qubita            local         
  247. template    qdebris1=qubitb            local         
  248. template    qdebris2=qubitc            local         
  249. template    qdebris3=qubitd            local         
  250. template    qdebris4=qubite            local         
  251.  
  252.  
  253.  
  254. # ===================Don's Added Killed Stuff=========================
  255.     thing        expos                        #position for exploding of wall.
  256.     surface    wall0
  257.     surface    wall1    
  258.     sound        wallexpl=gen_bazooka_fire.wav        local
  259.     int        pieces=10                    local
  260.     int        i                        local
  261.     thing        fragment                    local
  262.     int        curcam                    local
  263.     template    debris0=stoneshrapa            local         
  264.     template    debris1=stoneshrapb            local           
  265.     template    debris2=stoneshrapc            local         
  266.     template    debris3=stoneshrapas            local         
  267.     template    debris4=stoneshrapbs            local
  268.     sound        music0=mus_olv_snakedead.wav        local
  269.     thing        boss_hint
  270.     material    dustmat=gen_a4sfx_dustcloud.mat    local
  271.     template    dusttemp=dustcloud            local        
  272.     template    dustghost=ghost                local
  273.     thing        dustpos                    local
  274.     thing        dust                        local
  275.     thing        player                    local
  276.     thing        deathcampos
  277.     cog        hints
  278.  
  279. #    template    projectile=+dummy_debris        local
  280.  
  281.  
  282.  
  283. # ---------------- subroutines ---------------------------
  284.     flex        setindyonledge                local
  285.     flex        setindyJOH                    local
  286.     flex        spikemode                    local
  287.     flex        patrolmode                    local
  288.     flex        considerspit                local
  289.     flex        considertongue                local
  290.     flex        spit                        local
  291.     flex        setindynearestpatrolidx            local
  292.     flex        setatpatrolidx                local
  293.     flex        choosenextpatrolpt            local
  294.     flex        settoindy                    local
  295.     flex        settofutureindy                local
  296.     flex        setindynearestspikeidx            local
  297.     flex        findnearestspikepatrolpt        local
  298.     flex        advancetonextspikepatrolpt        local
  299.     flex        advancetonextpatrolpt            local
  300.     flex        choosenextspikeidx            local
  301.  
  302. end
  303.  
  304.  
  305. # ===================================================================
  306. code
  307.  
  308. # ...................................................................
  309. startup:
  310.  
  311.         // At some point in the future this message
  312.     // will have to be sent by one of don's cogs
  313.  
  314. //    cog_self = GetSelfCog();
  315. //    SendMessageEx(cog_self, 27, t_InMainTunnelPt, 0, 0, 0);        # (cogRef, User0, Location-Thing, 0, 0, 0)
  316.     t_Indy = GetLocalPlayerThing();
  317.  
  318.     n_PatrolDir    = 1;
  319.     b_IndyInOtherTunnel = 0;
  320.     b_IndyInMainTunnel = 0;
  321.     b_IndyOnLedge = 0;
  322.     b_IndyJOH = 0;
  323.     e_Mode = PATROL_MODE;    
  324.     n_idxDest = 0;
  325.     n_idxSpikeDest = 0;
  326.     n_SpikedIdx = -1;
  327.     b_OKToShowTongue = 0;
  328.  
  329.     t_UsableSpikePatrolPt00 = t_SpikePatrolPt00;
  330.     t_UsableSpikePatrolPt01 = t_SpikePatrolPt01;
  331.     t_UsableSpikePatrolPt02 = t_SpikePatrolPt02;
  332.     t_UsableSpikePatrolPt03 = t_SpikePatrolPt03;
  333.     t_UsableSpikePatrolPt04 = t_SpikePatrolPt04;
  334.  
  335.     return;
  336.  
  337.  
  338. # ...................................................................
  339. created:
  340.  
  341.     return;
  342.  
  343. # ...................................................................
  344. aievent:
  345.  
  346.     if ( GetSenderRef() != t_Boss ) return;
  347.     if (e_Mode == THRASH_MODE)
  348.     {
  349.         return;
  350.     }
  351.  
  352.     n_eventType        = GetParam(0);
  353.     n_CrntAIMode    = GetParam(1);
  354.     n_OldAIMode        = GetParam(2);    // only valid for certain n_eventType values
  355.  
  356.     if (n_eventType == 0x100)        // SITHAI_EVENTMODECHANGED
  357.     {
  358.         // Set PATROL mode if Boss switched to search mode (SITHAI_MODESEARCHING)
  359.         if ( !BITTEST(n_OldAIMode, 0x4) && BITTEST(n_CrntAIMode, 0x4) && !b_AvoidCallStackDepthOverflow )
  360.         {
  361.             call SetIndyOnLedge;
  362.             if (b_IndyOnLedge && (e_Mode != SPIKE_MODE))
  363.             {
  364.                 call SpikeMode;
  365.             }
  366.             else if (!b_IndyOnLedge && (e_Mode == STANDARD_MODE))
  367.             {
  368.                 call PatrolMode;
  369.             }
  370.             else
  371.             {
  372. //                DEBUGPRINT("AIEVENT:: Continuing in custom patrol mode");
  373.             }
  374.         }
  375.         // Boss acquired target visibility (SITHAI_MODETARGETVISIBLE).
  376.         else if ( !BITTEST(n_OldAIMode, 0x400) && BITTEST(n_CrntAIMode, 0x400) && !b_AvoidCallStackDepthOverflow)
  377.         {
  378.             call SetIndyOnLedge;
  379.             call SetIndyJOH;
  380.             if (b_IndyOnLedge || b_IndyJOH)
  381.             {
  382.                 if (e_Mode == SPIKE_MODE)
  383.                 {
  384.                     AIClearMode(t_Boss, 0x622); // turn off attacking, target visible, fire valid, active
  385.                     AISetMode(t_Boss, 0x04); // put in searching
  386.                 }
  387. //                DEBUGPRINT("AIEVENT:: Boss saw target on ledge");
  388.                 call SpikeMode;
  389.             }
  390.             else
  391.             {
  392. //                DEBUGPRINT("AIEVENT:: Boss re-acquired target");
  393.                 e_Mode = STANDARD_MODE;
  394.                 AISetGoalThing(t_Boss, GetLocalPlayerThing());
  395.             }
  396.         }
  397.  
  398.         // Check for lost sight of goal
  399.         if ( BITTEST(n_OldAIMode, 0x20000) && !BITTEST(n_CrntAIMode, 0x20000) )
  400.         {
  401.             if (e_Mode == PATROL_MODE)
  402.             {
  403. //                DEBUGPRINT("AIEVENT:: Boss lost sight of goal in PATROL mode");
  404.                 call PatrolMode;
  405.             }
  406.             else if (e_Mode == SPIKE_MODE)
  407.             {
  408. //                DEBUGPRINT("AIEVENT:: Boss lost sight of goal in SPIKE mode");
  409.                 e_Mode = STANDARD_MODE; // Will force SpikeMode to pick new dest
  410.                 call SpikeMode;
  411.             }
  412.             else
  413.             {
  414. //                DEBUGPRINT("AIEVENT:: Boss lost sight of Indy in STANDARD/THRASH mode");
  415.                 // Engine will switch us to searching mode if sight not regained soon
  416.             }
  417.         }
  418.  
  419.     }
  420.     else if (n_eventType == 0x800)    // SITHAI_EVENTGOALREACHED
  421.     {
  422.         call SetIndyOnLedge;
  423.         call SetIndyJOH;
  424.         if (b_IndyOnLedge || (e_Mode == SPIKE_MODE) || b_IndyJOH)
  425.         {
  426. //            DEBUGPRINT("AIEVENT:: SITHAI_EVENTGOALREACHED");
  427.             call SpikeMode;
  428.         }
  429.         else if (e_Mode == PATROL_MODE)
  430.         {
  431. //            DEBUGPRINT("AIEVENT:: SITHAI_EVENTGOALREACHED in PATROL");
  432.             call PatrolMode;
  433.         }
  434.         call ConsiderSpit;
  435.         // Calling ConsiderTongue right after consider spit so that
  436.         // he won't try to do both at same time and spitting gets priority
  437.         call ConsiderTongue;
  438.     }
  439.  
  440.     return;
  441.  
  442.  
  443. # ...................................................................
  444. timer:
  445.     n_TimerID = GetSenderID();
  446.     if (n_TimerID == TIMER_ID_WAKEUP_LITTLE_SNAKE)
  447.     {
  448.         // Force wake up with 0 duration
  449.         AISpat(GetParam(0), 0, 0);
  450.     }
  451.     else if (n_TimerID == TIMER_ID_RELOAD_LITTLE_SNAKE)
  452.     {
  453.         b_OKToLaunchLittleSnake = 1;
  454.     }
  455.     else if (n_TimerID == TIMER_ID_SPIT_LITTLE_SNAKE)
  456.     {
  457.         call Spit;
  458.     }
  459.     else if (n_TimerID == TIMER_ID_EXPLODE_TAIL_PIECE)
  460.     {
  461.         n_idx = GetParam(0);
  462.         n_idx2 = (MAX_SNAKE_PIECES - 1 - n_idx); // goes from 0 to MAX_SNAKE_PIECES - 1
  463. //        DEBUGFLEX(n_idx, "n_idx");
  464. //        DEBUGFLEX(n_idx2, "n_idx2");
  465.  
  466.         // For pieces, but not head
  467.         if (n_idx >= 1)
  468.         {
  469.             // Create more debris as we get closer to head
  470.             for (i = 0; i < ((n_idx2/4)+1); i = i + 1)
  471.             {
  472.                 fragment = CreateThing(qdebris0[RandBetween(0, 4)], t_Boss[n_idx]);                                 
  473.                 SetThingVel(fragment, VectorScale(VectorAdd(RandVec(), '-0.5 -0.5 0.5'), 2));
  474.                 SetThingRotVel(fragment, VectorScale(RandVec(), 900.0));                    
  475.             }
  476.     
  477.             // A final present from Quetz to Indy
  478.             if ( (n_LittleSnakesAlive < (MAX_LITTLE_SNAKES + 4)) && (RandBetween(1,100) < 20) )
  479.             {
  480.                fragment = CreateThing(tpl_LittleSnake, t_Boss[n_idx]); 
  481.                if (fragment != -1)
  482.                {
  483.                    n_LittleSnakesAlive = n_LittleSnakesAlive + 1;
  484.                    SetThingVel(fragment, VectorScale(VectorAdd(RandVec(), '-0.5 -0.5 0.2'), 1.5));
  485.                    AISpat(fragment, 1.0, TIMER_ID_WAKEUP_LITTLE_SNAKE);
  486.                }
  487.             }
  488.  
  489.             PlaySoundThing(wallexpl,  t_Boss[n_idx], 1, 5, 30, 0);
  490.             
  491.             // Destroy next piece closer to head in a slightly shorter time interval
  492.             f_Val1 = BASE_SEQUENTIAL_EXPLOSION_TIME - (n_idx2 * 0.01);
  493.             if (f_Val1 <= 0.05)
  494.             {
  495.                 f_Val1 = 0.05;
  496.             }
  497.             SetTimerEx(f_Val1 , TIMER_ID_EXPLODE_TAIL_PIECE, n_idx-1, 0 );
  498.  
  499.             CreateThing(tpl_ExplodePiece, t_Boss[n_idx]);
  500.             v_PosShake = VectorScale(VectorAdd(RandVec(), '-0.5 -0.5 -0.5'), 0.002);
  501.             v_PYRShake = VectorScale(VectorAdd(RandVec(), '-0.5 -0.5 -0.5'), 1.0);
  502.             SetPOVShake(v_PosShake, v_PYRShake, 1.0, 15.0);
  503.  
  504.         }
  505.         else // It's the head
  506.         {
  507.             for (i = 0; i < 12; i = i + 1)
  508.             {
  509.                 fragment = CreateThing(qdebris0[RandBetween(0, 4)], t_Boss[n_idx]);                                 
  510.                 if (fragment != -1)
  511.                 {
  512.                     // Reposition in middle of head
  513.                     v_Temp = VectorAdd(GetThingLVec(t_Boss), GetThingUVec(t_Boss));
  514.                     v_Temp = VectorScale(v_Temp, 0.1);
  515.                     f_Dist = VectorLen(v_Temp);
  516.                     v_Temp = VectorNorm(v_Temp);
  517.                     SetCollideType(fragment, 0); // don't collide with snake head
  518.                     MoveThing(fragment, v_Temp, f_Dist, 0.0);
  519.                     SetCollideType(fragment, 1);
  520.  
  521.                     // Launch it and spin it.
  522.                     SetThingVel(fragment, VectorScale(VectorAdd(RandVec(), '-0.5 -0.5 0.0'), 2));
  523.                     SetThingRotVel(fragment, VectorScale(RandVec(), 900.0));                    
  524.                 }
  525.             }
  526.     
  527.             PlaySoundThing(wallexpl,  t_Boss[n_idx], 1, 5, 30, 0);
  528.             
  529.             // Explosion effect
  530. //             DebugPrint("Boss Fragments");
  531.             fragment = CreateThing(tpl_ExplodeBoss, t_Boss[n_idx]);
  532.             if (fragment != -1)
  533.             {
  534.                 // Position in middle of head
  535.                 v_Temp = VectorAdd(GetThingLVec(t_Boss), GetThingUVec(t_Boss));
  536.                 v_Temp = VectorScale(v_Temp, 0.1);
  537.                 f_Dist = VectorLen(v_Temp);
  538.                 v_Temp = VectorNorm(v_Temp);
  539.                 MoveThing(fragment, v_Temp, f_Dist, 0.0);
  540.             }
  541.             
  542.             v_PosShake = VectorSet(0.005, 0.005, 0.005);
  543.             v_PYRShake = VectorSet(3.0, 2.0, 4.0);
  544.             SetPOVShake(v_PosShake, v_PYRShake, 2.0, 30.0);
  545.  
  546.             // Kill boss 
  547. //             DebugPrint("Final Boss Damage");
  548.             DamageThing(t_Boss, GetHealth(t_Boss), 0x400000, t_Indy);
  549.         }
  550.         
  551. //        DebugFlex(n_idx, "Destroy Thing");
  552.         DestroyThing(t_Boss[n_idx]);
  553.         t_Boss[n_idx] = -1;
  554.  
  555.     }
  556.     else if (n_TimerID == TIMER_ID_ENABLE_TONGUE)
  557.     {
  558.         b_OKToShowTongue = 1;
  559.     }
  560.  
  561.     return;
  562.  
  563. # ...................................................................
  564. touched:
  565. //    DEBUGPRINT("Touched:");
  566.  
  567. //    DebugFlex(n_SpikedIdx, "Touched: n_SpikedIdx");
  568. //    DebugFlex(GetSenderRef(), "Touched: GetSenderRef");
  569. //    DebugFlex(GetSourceRef(), "Touched: GetSourceRef");
  570.  
  571. //    DEBUGPRINT("Touched: Check for Quetz Piece");
  572.     // Make sure that a piece of quetz is involved
  573.     b_Temp = 0;
  574.     for (n_idx = 0; n_idx < MAX_SNAKE_PIECES; n_idx = n_idx + 1)
  575.     {
  576.         if (t_Boss[n_idx] == GetSenderRef())
  577.         {
  578.             b_Temp = 1;
  579.         }
  580.     }
  581.         
  582.     if (!b_Temp)
  583.     {
  584.         return;
  585.     }
  586.  
  587.     //    Damage indy if quetz touches him,
  588.     if ( (GetSourceRef() == t_Indy)
  589.          && b_OKToDamageIndy
  590.          )
  591.     {
  592.         DamageThing(t_Indy, 50, 0x01000000, t_Boss);
  593.         b_OKToDamageIndy=0;
  594.         Sleep(0.5); // so we don't damage him every frame
  595.         b_OKToDamageIndy=1;
  596.     }
  597.     // See if spike should damage Quetz
  598.     else if (n_SpikedIdx == -1)
  599.     {
  600. //        DEBUGPRINT("Touched: Check for Spike");
  601.         for (n_idx = 0; n_idx < NUM_SPIKES; n_idx = n_idx + 1)
  602.         {
  603.             if (t_SpikeTip00[n_idx] == GetSourceRef())
  604.             {
  605.                 n_SpikedIdx = n_idx;
  606.             }
  607.         }
  608.  
  609.         if (n_SpikedIdx != -1)
  610.         {
  611. //            DEBUGPRINT("Touched: Spiked");
  612.  
  613.             fragment = CreateThing(tpl_sparks, GetSourceRef());                                 
  614.             fragment = CreateThing(tpl_sparks, GetSourceRef());                                 
  615.  
  616.             // Stop movement sound
  617.             if (n_MoveSoundChannel != -1)
  618.             {
  619.                 StopSound(n_MoveSoundChannel, 0.25);
  620.                 n_MoveSoundChannel = -1;
  621.             }
  622.  
  623.             n_SpikeDamage = 0;
  624.             if (BITTEST(GetActorFlags(GetSenderRef()),0x200))            
  625.             {
  626. //                PRINT("Head Shot");
  627.                 n_SpikeDamage = HEAD_SHOT_DAMAGE;
  628.             }
  629.             else if (BITTEST(GetAttachFlags(GetSenderRef()),0x40))
  630.             {
  631. //                PRINT("Body Blow");
  632.                 n_SpikeDamage = BODY_SHOT_DAMAGE;
  633.             }
  634.  
  635.             // Just to make sure it was snake part that was
  636.             if (n_SpikeDamage) 
  637.             {
  638.                 // Reset usuable spike points
  639.                 t_UsableSpikePatrolPt00 = t_SpikePatrolPt00;
  640.                 t_UsableSpikePatrolPt01 = t_SpikePatrolPt01;
  641.                 t_UsableSpikePatrolPt02 = t_SpikePatrolPt02;
  642.                 t_UsableSpikePatrolPt03 = t_SpikePatrolPt03;
  643.     
  644.                 // Make most recent spike off limits
  645.                 t_UsableSpikePatrolPt00[n_SpikedIdx] = t_OffSpikePatrolPt00[n_SpikedIdx];
  646.     
  647.                 // Begin sequential explosion if going to die
  648.                 if (GetHealth(t_Boss) - n_SpikeDamage < 1.0)
  649.                 {
  650.                     SetTimerEx(BASE_SEQUENTIAL_EXPLOSION_TIME, TIMER_ID_EXPLODE_TAIL_PIECE, MAX_SNAKE_PIECES-1, 0 );
  651.                     n_SpikeDamage = HEAD_SHOT_DAMAGE; // make it a head shot so quetz is frozen long enough to fully explode
  652.                 }
  653.                 else
  654.                 {
  655.                     DamageThing(t_Boss, n_SpikeDamage, 0x400000, t_Indy);
  656.                 }
  657.  
  658.                 e_Mode = THRASH_MODE;
  659.                 AISetMode(t_Boss, 0x2000); // disable
  660.                 AIClearMode(t_Boss, 0x009);// turn off moving, turning
  661.                 SetActorFlags(t_Boss, 0x40000); // immobile
  662.                 StopThing(t_Boss);
  663.                 if (n_SpikeDamage == HEAD_SHOT_DAMAGE)
  664.                 {
  665.                     PlaySoundThing(hurt2, t_Boss, 1.0, 40.0, 50.0, 0x880);
  666.                     StartQuetzAnim(t_Boss, 3); // thrash
  667.                     Sleep(3.5);
  668.                 }
  669.                 else
  670.                 {
  671.                     PlaySoundThing(hurt1, t_Boss, 1.0, 40.0, 50.0, 0x880);
  672.                     StartQuetzAnim(t_Boss, 5); // thrash small
  673.                     Sleep(2.0);
  674.                 }
  675.                 AIClearMode(t_Boss, 0x2000);
  676.                 ClearActorFlags(t_Boss, 0x40000); // immobile
  677.                 e_Mode = STANDARD_MODE;
  678.                 n_SpikedIdx = -1; // can be spiked
  679.  
  680.                 // Restart move sound if not dead
  681.                 if (GetHealth(t_Boss) > 0)
  682.                 {
  683.                     n_MoveSoundChannel = PlaySoundThing(move, t_Boss, 1.0, 0.0, 70.0, 0x881);
  684. //                    ChangeSoundPitch(n_MoveSoundChannel, 0.70, 0.1);
  685.                 }
  686.  
  687.             }
  688.         }
  689.     }
  690.  
  691. //    DEBUGPRINT("Touched: Done");
  692.     
  693.     return;
  694.  
  695. # ...................................................................
  696. damaged:
  697.  
  698.  
  699.     return;
  700.  
  701. # ...................................................................
  702. entered:
  703.     t_Indy = GetLocalPlayerThing();
  704.     if (BITTEST( GetActorFlags(t_Indy), 0x80))
  705.     {
  706.         // if indy is invisible, then we don't know where he is
  707.         return;
  708.     }
  709.  
  710.     if ( (GetSenderRef() == s_MainTunnel)
  711.          && (GetSourceRef() == t_Indy)
  712.          )
  713.     {
  714.         b_IndyInMainTunnel = 1;
  715. //        DEBUGPRINT("Indy in main tunnel");
  716.     }
  717.  
  718.     if ( (GetSenderRef() == s_OtherTunnel)
  719.          && (GetSourceRef() == t_Indy)
  720.          )
  721.     {
  722.         b_IndyInOtherTunnel = 1;
  723. //        DEBUGPRINT("Indy in small tunnel");
  724.     }
  725.  
  726.     // If patrolling
  727.     // and Indy has entered a section of interest
  728.     // then patrol toward where indy is
  729.     
  730.     if ( (GetSenderRef() == s_OtherTunnel || GetSenderRef() == s_OtherTunnel)
  731.          && GetSourceRef() == t_Indy
  732.          && (e_Mode == PATROL_MODE)
  733.          )
  734.     {
  735.         call PatrolMode;
  736.     }
  737.  
  738.     return;
  739.  
  740. # ...................................................................
  741. exited:
  742.     
  743.     if ( (GetSenderRef() == s_MainTunnel)
  744.          && (GetSourceRef() == t_Indy)
  745.          )
  746.     {
  747.         b_IndyInMainTunnel = 0;
  748. //        DEBUGPRINT("Indy leaving main tunnel");
  749.     }
  750.     
  751.     if ( (GetSenderRef() == s_OtherTunnel)
  752.          && (GetSourceRef() == t_Indy)
  753.          )
  754.     {
  755.         b_IndyInOtherTunnel = 0;
  756. //        DEBUGPRINT("Indy leaving small tunnel");
  757.     }
  758.  
  759.     return;
  760.  
  761. # ...................................................................
  762. // Create the boss at the ghost objects position, 
  763. // then create and attach all the tail pieces
  764. user0:
  765.  
  766.     # Create Quetz at location-THING == GetParam(0) [must be a valid THING]
  767.     t_Boss = CreateThing(tpl_quetz00, GetParam(0));
  768.     CaptureThing(t_Boss);
  769.  
  770. # ------------ Added by Don ------------------------
  771.     AttachThingToThing(boss_hint, t_Boss);
  772.     AISetCutsceneMode(t_Boss);
  773.  
  774. //    DEBUGPRINT("olv_quetzalcoat creating boss");
  775.  
  776.     n_idx = 1;
  777.     while( n_idx < MAX_SNAKE_PIECES )
  778.     {
  779. //        DEBUGPRINT("Create Piece");
  780.         t_Boss[n_idx] = CreateThing(tpl_quetz00[n_idx], t_Boss[n_idx-1]);
  781. //        DEBUGPRINT("Capture Piece");
  782.         CaptureThing(t_Boss[n_idx]);    
  783.         n_idx = n_idx + 1;
  784.     }
  785.  
  786.     # Attach in reverse order because newly attached piece gets placed first in list    
  787.     n_idx = MAX_SNAKE_PIECES-1;    
  788.     while( n_idx > 0 )
  789.     {
  790. //        DEBUGPRINT("Attach Piece");
  791.         AttachThingToThing(t_Boss[n_idx], t_Boss);
  792. //        DEBUGPRINT("Get Attach flags for Piece");
  793.         attachFlags = GetAttachFlags(t_Boss[n_idx]);
  794. //        DEBUGPRINT("Set Attach flags for Piece");
  795.         SetThingAttachFlags(t_Boss[n_idx], attachFlags | 0x0048); # SITH_ATTACH_NOMOVE | SITH_ATTACH_TAIL    
  796.         n_idx = n_idx - 1;
  797.     }
  798.  
  799.     SetTimerEx(TONGUE_RELOAD, TIMER_ID_ENABLE_TONGUE, 0, 0);
  800.  
  801.     ReturnEx(t_Boss);    # return Boss THING index to caller
  802.  
  803.  
  804. //    DebugFlex(t_Boss, "User0: t_Boss");
  805. //    DebugFlex(t_Indy, "User0: t_Indy");
  806. //    DebugFlex(t_SpikeTip00, "User0: t_SpikeTip00");
  807. //    DebugFlex(t_SpikeTip01, "User0: t_SpikeTip01");
  808. //    DebugFlex(t_SpikeTip02, "User0: t_SpikeTip02");
  809. //    DebugFlex(t_SpikeTip03, "User0: t_SpikeTip03");
  810.  
  811.     // These lines are for Steve to use only during testing
  812. //    Sleep(10.0);    //wait for door to finish
  813. //    SendMessageEx(GetSelfCog(), 28, 1, 0, 0, 0);    
  814.  
  815.     return;
  816.  
  817.  
  818.  
  819. # ===================================================================
  820. #    Subroutines
  821. # ===================================================================
  822.  
  823. # ...................................................................
  824. // Figure out where we are and then head in patrol direction that will
  825. // get us to Indy as quickly as possible
  826. PatrolMode:
  827.  
  828.     if (e_Mode != PATROL_MODE)
  829.     {
  830. //        DEBUGPRINT("Entering PATROL mode");
  831.         e_Mode = PATROL_MODE;
  832.  
  833.         b_AvoidCallStackDepthOverflow = 1;
  834.         AIClearMode(t_Boss, 0x622); // turn off attacking, target visible, fire valid, active
  835.         AISetMode(t_Boss, 0x04); // put in searching
  836.         b_AvoidCallStackDepthOverflow = 0;
  837.     }
  838.  
  839.     call SetIndyNearestPatrolIdx; 
  840.     call SetAtPatrolIdx;
  841.  
  842.     // If Indy is not invisible we can know where he is
  843.     if (n_IndyNearestPatrolIdx != -1)
  844.     {
  845.  
  846.         if (n_AtPatrolIdx == NUM_PATROL_POINTS)
  847.         {
  848.             // means we are in the main tunnel
  849.             // so force him to get out
  850.             n_PatrolDir = 1;
  851.         }
  852.         else
  853.         {
  854.             // Figure out which way to go around arena to get to Indy quickest
  855.             // If index diff equals (NUM_PATROL_POINTS * 0.5), then
  856.             // Quetz is halfway around arena from Indy, so assume existing
  857.             // patrol dir is good
  858.             n_PatrolPtIndexDiff = n_AtPatrolIdx - n_IndyNearestPatrolIdx;
  859.             if ( ABS(n_PatrolPtIndexDiff) != (NUM_PATROL_POINTS * 0.5))
  860.             {
  861.                 if (n_PatrolPtIndexDiff > (NUM_PATROL_POINTS * 0.5))
  862.                 {
  863.                     n_PatrolDir = 1;
  864.                 }
  865.                 else if (n_PatrolPtIndexDiff >= 0.0)
  866.                 {
  867.                     n_PatrolDir = -1;
  868.                 }
  869.                 else if (n_PatrolPtIndexDiff > -(NUM_PATROL_POINTS * 0.5))
  870.                 {
  871.                     n_PatrolDir = 1;
  872.                 }
  873.                 else
  874.                 {
  875.                     n_PatrolDir = -1;
  876.                 }
  877.             }
  878.         }
  879.     }
  880.  
  881.     call ChooseNextPatrolPt;
  882.     call AdvanceToNextPatrolPt;
  883.  
  884.     return;
  885.  
  886. # ...................................................................
  887. // Find closest patrol point, put in n_AtPatrolPoint
  888. // Includes point in main tunnel
  889. SetAtPatrolIdx:
  890.     
  891.     n_AtPatrolIdx = 0;
  892.     for (n_idx=1; n_idx <= NUM_PATROL_POINTS; n_idx=n_idx + 1)
  893.     {
  894.         // Compare test choice to 'current' choice
  895.         f_Val1 = VectorDist( GetThingPos(t_PatrolPt00[n_idx]), GetThingPos(t_Boss) );
  896.         f_Val2 = VectorDist( GetThingPos(t_PatrolPt00[n_AtPatrolIdx]), GetThingPos(t_Boss) );
  897.  
  898.         // If closer, make that our current choice
  899.         if ( f_Val1 < f_Val2 )
  900.         {
  901.             n_AtPatrolIdx = n_idx;
  902.         }
  903.     }
  904.  
  905. //    DEBUGFLEX(n_AtPatrolIdx,"- SetAtPatrolPt:");
  906.     
  907.     return;
  908.  
  909.  
  910. # ...................................................................
  911. // Set n_IndyNearestPatrolIdx to index of patrol point closest to Indy
  912. // If Indy is invisible set to -1
  913. SetIndyNearestPatrolIdx:
  914.  
  915.     if (BITTEST( GetActorFlags(t_Indy), 0x80))
  916.     {
  917.         n_IndyNearestPatrolIdx = -1;
  918.         return;
  919.     }
  920.  
  921.     n_IndyNearestPatrolIdx = 0;
  922.     for (n_idx=1; n_idx < NUM_PATROL_POINTS; n_idx=n_idx + 1)
  923.     {
  924.         // Compare test choice to 'current' choice
  925.         f_Val1 = VectorDist( GetThingPos(t_PatrolPt00[n_idx]), GetThingPos(t_Indy) );
  926.         f_Val2 = VectorDist( GetThingPos(t_PatrolPt00[n_IndyNearestPatrolIdx]), GetThingPos(t_Indy) );
  927.  
  928.         // If closer, make that our current choice
  929.         if ( f_Val1 < f_Val2 )
  930.         {
  931.             n_IndyNearestPatrolIdx = n_idx;
  932.         }
  933.     }
  934.     
  935. //    DEBUGFLEX(n_IndyNearestPatrolIdx,"SetIndyNearestPatrolIdx:");
  936.     
  937.     return;
  938.  
  939.  
  940.  
  941. # ...................................................................
  942. // Choose a new destination
  943. // Based on n_PatrolDir and n_AtPatrolIdx
  944. ChooseNextPatrolPt:
  945.     
  946. //    DEBUGFLEX(n_AtPatrolIdx, "ChooseNextPatrolPt: n_AtPatrolIdx");
  947. //    DEBUGFLEX(n_PatrolDir, "ChooseNextPatrolPt: n_PatrolDir");
  948.  
  949.     // If at entrance to tunnel and Indy is known to be in tunnel
  950.     // then drive into tunnel
  951.     if ( (n_AtPatrolIdx == 0)
  952.          && (b_IndyInMainTunnel)
  953.          )
  954.     {
  955.         n_idxDest = NUM_PATROL_POINTS; 
  956.     }
  957.     else
  958.     {
  959.         // Otherwise advance in patrol direction
  960.         // But skip intermediate patrol points
  961.         if (BITTEST(n_AtPatrolIdx,0x1))
  962.         {
  963.             // we are at an ODD/corner patrol point, so advance by two to next corner
  964.             n_idxDest = n_AtPatrolIdx + n_PatrolDir + n_PatrolDir;
  965.         }
  966.         else
  967.         {
  968.             // At intermediate index, advance by only one
  969.             n_idxDest = n_AtPatrolIdx + n_PatrolDir;
  970.         }
  971.     
  972.         if (n_idxDest < 0)
  973.             n_idxDest = NUM_PATROL_POINTS - 1;
  974.         else if( n_idxDest >= NUM_PATROL_POINTS )
  975.             n_idxDest = 0;
  976.     }
  977.  
  978.  
  979. //    DEBUGFLEX(n_idxDest,"-- ChooseNextPatrolPt: New dest");
  980.  
  981.     return;
  982.  
  983.  
  984. # ...................................................................
  985. AdvanceToNextPatrolPt:
  986.  
  987. //    DEBUGFLEX(n_idxDest,"--- AdvanceToNextPatrolPt:: Advancing to patrol pt");
  988.     AISetGoalThing(t_Boss, t_PatrolPt00[n_idxDest]);
  989.  
  990.     return;
  991.  
  992.  
  993. # ...................................................................
  994. // Sets b_IndyOnLedge
  995. SetIndyOnLedge:
  996.  
  997.     indyPos = GetThingPos(t_Indy);
  998.     bossPos = GetThingPos(t_Boss);
  999.  
  1000.     zDiff = VectorZ(indyPos) - VectorZ(bossPos);            
  1001. //    DebugFlex(zDiff, "zDiff");
  1002.     if (zDiff > 0.2) // doesn't include puzzle block as on ledge
  1003.     {
  1004.         b_IndyOnLedge = 1;
  1005.     }
  1006.     else
  1007.     {
  1008.         b_IndyOnLedge = 0;
  1009.     }
  1010.     
  1011.     return;
  1012.  
  1013. # ...................................................................
  1014. // Sets b_IndyJOH
  1015. SetIndyJOH:
  1016.  
  1017.     b_IndyJOH = GetMoveStatus(t_Indy);
  1018.     if ( (b_IndyJOH == 87) || (b_IndyJoh == 88))
  1019.     {
  1020.         b_IndyJOH =1 ;
  1021.     }
  1022.     else
  1023.     {
  1024.         b_IndyJOH = 0;
  1025.     }
  1026.     
  1027.     return;
  1028.  
  1029. # ...................................................................
  1030. // Sets v_ToIndy - a normalized vector from boss to indy
  1031. // Sets v_ToIndyFlat - a normalized vector from boss to indy, with no z
  1032. // Set f_DistToIndy
  1033. SetToIndy:
  1034.  
  1035.     bossPos = GetThingPos(t_Boss);
  1036.     indyPos = GetThingPos(t_Indy);
  1037.  
  1038.     v_ToIndy = VectorSub(indyPos, bossPos);
  1039.     v_ToIndy = VectorNorm(v_ToIndy);
  1040.     v_ToIndyFlat = VectorSet(VectorX(v_ToIndy), VectorY(v_ToIndy), 0.0);
  1041.     v_ToIndyFlat = VectorNorm(v_ToIndyFlat);
  1042.     f_DistToIndy = VectorDist(indyPos, bossPos);
  1043.  
  1044.     return;
  1045.  
  1046.  
  1047. # ...................................................................
  1048. // Expects f_FutureDelta and b_IndyOnLedge to be set
  1049. // Sets v_ToFutureIndy - a normalized vector from boss to indy pos in the future
  1050. // Sets v_ToFutureIndyFlat - a normalized vector from boss to indy pos in future, with no z
  1051. // Sets f_DistToFutureIndy
  1052. SetToFutureIndy:
  1053.  
  1054.     bossPos = GetThingPos(t_Boss);
  1055.     indyPos = GetThingPos(t_Indy);
  1056.     call SetIndyNearestSpikeIdx;
  1057.  
  1058.     if (!b_IndyOnLedge)
  1059.     {
  1060.         v_IndyVel = GetThingVel(t_Indy);
  1061.         v_Temp = VectorScale(v_IndyVel, f_FutureDelta);
  1062.         indyPos = VectorAdd(indyPos, v_Temp);
  1063.     
  1064.     }
  1065.     // If Indy is on ledge, ignore his velocity, 
  1066.     // just aim several meters away from Indy on ledge he is on
  1067.     else
  1068.     {
  1069.         if (n_IndyNearestSpikeIdx == 0)
  1070.         {
  1071.             v_Temp = VectorSub(GetThingPos(t_SpikePatrolPt01), GetThingPos(t_SpikePatrolPt00)); 
  1072.             v_Temp = VectorNorm(v_Temp);
  1073.         }
  1074.         else if (n_IndyNearestSpikeIdx == 1)
  1075.         {
  1076.             v_Temp = VectorSub(GetThingPos(t_SpikePatrolPt00), GetThingPos(t_SpikePatrolPt01)); 
  1077.             v_Temp = VectorNorm(v_Temp);
  1078.         }
  1079.         else if (n_IndyNearestSpikeIdx == 2)
  1080.         {
  1081.             v_Temp = VectorSub(GetThingPos(t_SpikePatrolPt03), GetThingPos(t_SpikePatrolPt02)); 
  1082.             v_Temp = VectorNorm(v_Temp);
  1083.         }
  1084.         else if (n_IndyNearestSpikeIdx == 3)
  1085.         {
  1086.             v_Temp = VectorSub(GetThingPos(t_SpikePatrolPt02), GetThingPos(t_SpikePatrolPt03)); 
  1087.             v_Temp = VectorNorm(v_Temp);
  1088.         }
  1089.  
  1090.         indyPos = VectorAdd(indyPos, VectorScale(v_Temp, .3));
  1091.     }
  1092.     
  1093.     v_ToFutureIndy = VectorSub(indyPos, bossPos);
  1094.     v_ToFutureIndy = VectorNorm(v_ToFutureIndy);
  1095.     v_ToFutureIndyFlat = VectorSet(VectorX(v_ToFutureIndy), VectorY(v_ToFutureIndy), 0.0);
  1096.     v_ToFutureIndyFlat = VectorNorm(v_ToFutureIndyFlat);
  1097.  
  1098.     f_DistToFutureIndy = VectorDist(indyPos, bossPos);
  1099.     return;
  1100.  
  1101.  
  1102.  
  1103. # ...................................................................
  1104. SpikeMode:
  1105.     // Just entering spike mode
  1106. //    DebugPrint("Spike Mode");
  1107.     if (e_Mode != SPIKE_MODE)
  1108.     {
  1109. //        DEBUGPRINT("Entering SPIKE mode");
  1110.         e_Mode = SPIKE_MODE;
  1111.  
  1112.         b_AvoidCallStackDepthOverflow = 1;
  1113.         AIClearMode(t_Boss, 0x622); // turn off attacking, target visible, fire valid, active
  1114.         AISetMode(t_Boss, 0x04); // put in searching
  1115.         b_AvoidCallStackDepthOverflow = 0;
  1116.         
  1117.         // Go to closest spike patrol point
  1118.         call FindNearestSpikePatrolPt;
  1119.         call AdvanceToNextSpikePatrolPt;
  1120.     }
  1121.     else
  1122.     {
  1123.         // If at desination, choose a new dest
  1124.         //
  1125.         f_Val1 = 1.0 * GetThingMoveSize(t_Boss);
  1126.         if ( VectorDist(GetThingPos(t_Boss), GetThingPos(t_UsableSpikePatrolPt00[n_idxSpikeDest])) <= f_Val1 )
  1127.         {
  1128.             n_AtSpikeIdx = n_idxSpikeDest;
  1129.             call SetIndyNearestSpikeIdx;
  1130.  
  1131.             n_idx2 = n_AtSpikeIdx - n_IndyNearestSpikeIdx;
  1132.             
  1133.             // If Q not at point in front of main tunnel
  1134.             if (n_AtSpikeIdx != 4)
  1135.             {
  1136.                 if (n_idx2 == 3)
  1137.                 {
  1138.                     n_PatrolDir = 1;// cw
  1139.                 }
  1140.                 else if (n_idx2 == 1)
  1141.                 {
  1142.                     n_PatrolDir = -1;// ccw
  1143.                 }
  1144.                 else if (n_idx2 == -1)
  1145.                 {
  1146.                     n_PatrolDir = 1;// cw
  1147.                 }
  1148.                 else if (n_idx2 == -3)
  1149.                 {
  1150.                     n_PatrolDir = -1;// ccw
  1151.                 }
  1152.             }
  1153.             // Q at point in front of main tunnel
  1154.             else
  1155.             {
  1156.                 if (n_IndyNearestSpikeIdx <= 1)
  1157.                 {
  1158.                     n_PatrolDir = 1;// cw
  1159.                 }
  1160.                 else
  1161.                 {
  1162.                     n_PatrolDir = -1;// ccw
  1163.                 }
  1164.             }
  1165.             
  1166.  
  1167.             // If one point away from Indy, go toward him
  1168.             // otherwise continue in same direction
  1169.             if (n_IndyNearestSpikeIdx - n_AtSpikeIdx == 1)
  1170.             {
  1171.                 n_PatrolDir = 1;
  1172.             }
  1173.             else if (n_IndyNearestSpikeIdx - n_AtSpikeIdx == -1)
  1174.             {
  1175.                 n_PatrolDir = -1;
  1176.             }
  1177.  
  1178.             call ChooseNextSpikeIdx;
  1179.             call AdvanceToNextSpikePatrolPt;
  1180.         }
  1181.     }
  1182.  
  1183.     // In the event the Quetz saw indy on a ledge while not in SPIKE_MODE, 
  1184.     // the goal thing needs to be fixed up
  1185.     if (AIGetGoalThing(t_Boss) != t_UsableSpikePatrolPt00[n_idxSpikeDest])
  1186.     {
  1187.         AISetGoalThing(t_Boss, t_UsableSpikePatrolPt00[n_idxSpikeDest]);
  1188.     }
  1189.  
  1190. //    DebugPrint("Spike Mode: Done");
  1191.     
  1192.     return;
  1193.  
  1194.  
  1195. # ...................................................................
  1196. // Set n_IndyNearestSpikeIdx to index of spike point closest to Indy
  1197. // If Indy is invisible set to -1
  1198. SetIndyNearestSpikeIdx:
  1199.  
  1200. //    if (BITTEST( GetActorFlags(t_Indy), 0x80))
  1201. //    {
  1202. //        n_IndyNearestSpikeIdx = -1;
  1203. //        return;
  1204. //    }
  1205.  
  1206.     n_IndyNearestSpikeIdx = 0;
  1207.     // Ignore last point, which is the one in front of the main tunnel
  1208.     for (n_idx=1; n_idx < NUM_SPIKE_POINTS-1; n_idx=n_idx + 1)
  1209.     {
  1210.         // Compare test choice to 'current' choice
  1211.         f_Val1 = VectorDist( GetThingPos(t_UsableSpikePatrolPt00[n_idx]), GetThingPos(t_Indy) );
  1212.         f_Val2 = VectorDist( GetThingPos(t_UsableSpikePatrolPt00[n_IndyNearestSpikeIdx]), GetThingPos(t_Indy) );
  1213.  
  1214.         // If closer, make that our current choice
  1215.         if ( f_Val1 < f_Val2 )
  1216.         {
  1217.             n_IndyNearestSpikeIdx = n_idx;
  1218.         }
  1219.     }
  1220.     
  1221. //    DEBUGFLEX(n_IndyNearestSpikeIdx,"SetIndyNearestSpikeIdx:");
  1222.     
  1223.     return;
  1224.  
  1225.  
  1226. # ...................................................................
  1227. // Choose a new destination
  1228. // Based on n_PatrolDir and n_AtSpikeIdx
  1229. ChooseNextSpikeIdx:
  1230.     
  1231. //    DEBUGFLEX(n_AtSpikeIdx, "ChooseNextSpikeIdx: n_AtSpikeIdx");
  1232. //    DEBUGFLEX(n_PatrolDir, "ChooseNextSpikeIdx: n_PatrolDir");
  1233.  
  1234.     n_idxSpikeDest = n_AtSpikeIdx + n_PatrolDir;
  1235.     
  1236.     // Skips that last point in the array, which is in
  1237.     // front of the main tunnel. Using it makes the snake wiggle less
  1238.     if (n_idxSpikeDest < 0)
  1239.         n_idxSpikeDest = NUM_SPIKE_POINTS - 2;
  1240.     else if( n_idxSpikeDest >= NUM_SPIKE_POINTS - 1 ) 
  1241.         n_idxSpikeDest = 0;
  1242.  
  1243. //    DEBUGFLEX(n_idxSpikeDest,"-- ChooseNextSpikeIdx: New dest");
  1244.  
  1245.     return;
  1246.  
  1247.  
  1248. # ...................................................................
  1249. AdvanceToNextSpikePatrolPt:
  1250.  
  1251. //    DEBUGFLEX(n_idxSpikeDest,"--- AdvanceToNextSpikePatrolPt:");
  1252.     AISetGoalThing(t_Boss, t_UsableSpikePatrolPt00[n_idxSpikeDest]);
  1253.  
  1254.     return;
  1255.  
  1256.  
  1257. # ...................................................................
  1258. // Find usable spike pt closest to Q current position.  Includes
  1259. // last pt in spike list, which is the point in front of the tunnel
  1260. FindNearestSpikePatrolPt:
  1261.  
  1262.     n_idxSpikeDest = 0;
  1263.     for (n_idx=1; n_idx < NUM_SPIKE_POINTS; n_idx=n_idx + 1)
  1264.     {
  1265.         // Compare test choice to 'current' choice
  1266.         f_Val1 = VectorDist( GetThingPos(t_UsableSpikePatrolPt00[n_idx]), GetThingPos(t_Boss) );
  1267.         f_Val2 = VectorDist( GetThingPos(t_UsableSpikePatrolPt00[n_idxSpikeDest]), GetThingPos(t_Boss) );
  1268.  
  1269.         // If closer, make that our current choice
  1270.         if ( f_Val1 < f_Val2 )
  1271.         {
  1272.             n_idxSpikeDest = n_idx;
  1273.         }
  1274.     }
  1275.  
  1276. //    DEBUGFLEX(n_idxSpikeDest,"- FindNearestSpikePatrolPt:");
  1277.     
  1278.     return;
  1279.  
  1280.  
  1281.  
  1282. # ...................................................................
  1283. ConsiderSpit:
  1284. //    DEBUGPRINT("ConsiderSpit:");
  1285.     if (!b_OKToLaunchLittleSnake)
  1286.     {
  1287.         return;
  1288.     }
  1289.     if (n_LittleSnakesAlive >= MAX_LITTLE_SNAKES)
  1290.     {
  1291.         return;
  1292.     }
  1293.  
  1294.     // Is Indy invisible
  1295.     if (BITTEST( GetActorFlags(t_Indy), 0x80))
  1296.     {
  1297.         return;
  1298.     }
  1299.  
  1300.     // Don't let quetz spit when he is in the main tunnel.
  1301.     // This prevents him from spitting a snake up on to 
  1302.     // the platform which holds the imp part. Little snake looks silly up there.
  1303.     call SetAtPatrolIdx;
  1304.     if (n_AtPatrolIdx == NUM_PATROL_POINTS)
  1305.     {
  1306.         return;
  1307.     }
  1308.  
  1309.     // This keeps quetz from spitting snakes on to little
  1310.     // pedestal and end of tunnel. That looks bad.
  1311.     if (b_IndyInMainTunnel)
  1312.     {
  1313.         return;
  1314.     }
  1315.  
  1316.     // Don't spit while Indy is using the Jewel of Heaven
  1317.     call SetIndyJOH;
  1318.     if (b_IndyJOH)
  1319.     {
  1320.         return;
  1321.     }
  1322.  
  1323.     call SetToIndy;
  1324.     call SetIndyOnLedge;
  1325.     
  1326. //    Print("Dist");
  1327. //    PrintFlex(f_DistToIndy);
  1328.     if (b_IndyOnLedge)
  1329.     {
  1330.         if (f_DistToIndy < 1.8 || f_DistToIndy > 2.5)
  1331.         {
  1332.             return;
  1333.         }
  1334.         if (RandBetween(1,100) > 80)
  1335.         {
  1336.             return;
  1337.         }
  1338.     }
  1339.     else // not on ledge
  1340.     {
  1341.         if (f_DistToIndy < 1.0 || f_DistToIndy > 2.0)
  1342.         {
  1343.             return;
  1344.         }
  1345.         // Save little snakes for when Indy is on ledge
  1346.         if (n_LittleSnakesAlive <= (MAX_LITTLE_SNAKES/2))
  1347.         {
  1348.             if (RandBetween(1,100) > 60)
  1349.             {
  1350.                 return;
  1351.             }
  1352.         }
  1353.         else
  1354.         {
  1355.             return;
  1356.         }
  1357.     }
  1358.  
  1359.     v_BossLook = GetThingLVec(t_Boss);
  1360.     f_Dot = VectorDot(v_BossLook, v_ToIndyFlat);
  1361.  
  1362. //    Print("Dot");
  1363. //    PrintFlex(f_Dot);
  1364.     if (f_Dot < 0.7)
  1365.     {
  1366.         return;
  1367.     }
  1368.  
  1369.     if (HasLos(t_Boss, t_Indy))
  1370.     {
  1371.         b_OKToLaunchLittleSnake = 0;
  1372.         // Buy us a little time to run animation
  1373.         SetTimerEx(0.24, TIMER_ID_SPIT_LITTLE_SNAKE, 0, 0);
  1374.  
  1375.         StartQuetzAnim(t_Boss, 2);
  1376.     }
  1377.  
  1378.  
  1379. //    DEBUGPRINT("ConsiderSpit: Done");
  1380.     return;
  1381.  
  1382.  
  1383. #=====================================================
  1384. Spit:
  1385. //    DEBUGPRINT("Spit:");
  1386. //    PRINT("Spit");
  1387.  
  1388.     call SetToIndy;
  1389. //    PrintFlex(f_DistToIndy);
  1390.  
  1391.     // Find an empty slot        
  1392.     n_LittleSnakeIdx = -1;
  1393.     for (n_idx=0; n_idx < MAX_LITTLE_SNAKES; n_idx=n_idx + 1)
  1394.     {
  1395.         if (t_LittleSnake00[n_idx] == -1)
  1396.         {
  1397.             n_LittleSnakeIdx = n_idx;
  1398.         }
  1399.     }
  1400.  
  1401.     if (n_LittleSnakeIdx == -1)
  1402.     {
  1403.         Print("No slots available for little snakes");
  1404.         return;
  1405.     }
  1406.  
  1407.     t_LittleSnake00[n_LittleSnakeIdx] = CreateThing(tpl_LittleSnake, t_Boss);
  1408.     if (t_LittleSnake00[n_LittleSnakeIdx] != -1)
  1409.     {
  1410.         CaptureThing(t_LittleSnake00[n_LittleSnakeIdx]);
  1411.         PlaySoundThing(spit, t_Boss, 1.0, 0.0, 50.0, 0x880);
  1412.  
  1413.         // Position little snake
  1414.         // somewhere in front of quetz's mouth
  1415.         v_Temp = VectorScale(v_ToIndyFlat, 0.1);
  1416.         v_Temp = VectorSet(VectorX(v_Temp), VectorY(v_Temp), 0.1);
  1417.         f_Dist = VectorLen(v_Temp);
  1418.         v_Temp = VectorNorm(v_Temp);
  1419.         MoveThing(t_LittleSnake00[n_LittleSnakeIdx], v_Temp, f_Dist, 0.0);
  1420.     
  1421.         // Launch little snake towards where indy will be
  1422.         // Use lots of empirical values to handle various situations
  1423.         // Indy on ground
  1424.         // Indy on ledge, heading straight toward ledge
  1425.         // Indy on ledge, coming in at angle to ledge
  1426.  
  1427.         f_Error = (Rand() - 0.5) * .07;
  1428.         f_FutureDelta = f_DistToIndy * 1.2; // time in future to predict indies position
  1429.         call SetIndyOnLedge; 
  1430.         call SetToFutureIndy;
  1431.         if (b_IndyOnLedge)
  1432.         {
  1433.             // Determine whether head on or glancing approach to Indy on ledge
  1434.             b_HeadOnLaunch = 1; // assume
  1435.             if ( (n_PatrolDir == 1)
  1436.                  &&( (n_idxSpikeDest == 1) || (n_idxSpikeDest == 3))
  1437.                  )
  1438.             {
  1439. //                PrintInt(n_idxSpikeDest);
  1440.                 b_HeadOnLaunch = 0;
  1441.             }
  1442.             if ( (n_PatrolDir == -1)
  1443.                  &&( (n_idxSpikeDest == 0) || (n_idxSpikeDest == 2))
  1444.                  )
  1445.             {
  1446. //                PrintInt(n_idxSpikeDest);
  1447.                 b_HeadOnLaunch = 0;
  1448.             }
  1449.  
  1450.             if (b_HeadOnLaunch)
  1451.             {
  1452. //                Print("Head on");
  1453.                 f_Val2 = 1.95 - f_DistToIndy;
  1454.                 f_Val2 = f_Val2 * 0.275;
  1455.                 f_Force = f_DistToFutureIndy * (52.5 + f_Val2);
  1456.                 
  1457.                 f_Val2 = 1.95 - f_DistToIndy;
  1458.                 f_Val2 = f_Val2 * 0.35;
  1459.                 v_Temp = VectorSet(VectorX(v_ToFutureIndyFlat), VectorY(v_ToFutureIndyFlat), (0.63 + f_Val2));
  1460.             }
  1461.             else
  1462.             {
  1463. //                Print("Glancing");
  1464.                 f_Val2 = 1.95 - f_DistToIndy;
  1465.                 f_Val2 = f_Val2 * 0.275;
  1466.                 f_Force = f_DistToFutureIndy * (50.0 + f_Val2);
  1467.                 
  1468.                 f_Val2 = 1.95 - f_DistToIndy;
  1469.                 f_Val2 = f_Val2 * 0.35;
  1470.                 v_Temp = VectorSet(VectorX(v_ToFutureIndyFlat), VectorY(v_ToFutureIndyFlat), (0.80 + f_Val2));
  1471.  
  1472.                 // Reduce error, this is a tough shot
  1473.                 f_Error = f_Error * 0.25;
  1474.             }
  1475.         }
  1476.         else
  1477.         {
  1478.             f_Force = f_DistToFutureIndy * 40.0;
  1479.             v_Temp = VectorSet(VectorX(v_ToFutureIndyFlat), VectorY(v_ToFutureIndyFlat), 0.5);
  1480.         }
  1481.         v_Temp = VectorNorm(v_Temp);
  1482.         v_BossRight = GetThingRVec(t_Boss);
  1483.         v_BossRight = VectorScale(v_BossRight, f_Error);
  1484.         v_Temp = VectorAdd(v_Temp, v_BossRight); // a little error
  1485.         v_Temp = VectorScale(v_Temp, f_Force);
  1486.         ApplyForce(t_LittleSnake00[n_LittleSnakeIdx], v_Temp);
  1487.         
  1488.         // Orient towards indy, but flat
  1489.         SetThingLook(t_LittleSnake00[n_LittleSnakeIdx], v_ToFutureIndyFlat);
  1490.  
  1491.         // Spit sprite
  1492. //        fragment = CreateThing(tpl_spit, t_LittleSnake00[n_LittleSnakeIdx]);
  1493. //        MaterialAnim(spit_mat, 8.0, 0x80000);
  1494.  
  1495.         // Set timer to wakeup snake after it lands
  1496.         AISpat(t_LittleSnake00[n_LittleSnakeIdx], 1.4, TIMER_ID_WAKEUP_LITTLE_SNAKE);
  1497.         n_LittleSnakesAlive = n_LittleSnakesAlive + 1;
  1498.  
  1499.         // Prevent Quetz from launching another for a few seconds
  1500.         SetTimerEx(10.0, TIMER_ID_RELOAD_LITTLE_SNAKE, 0, 0);
  1501.  
  1502.     }
  1503.  
  1504. //    DEBUGPRINT("Spit: Done");
  1505.     return; 
  1506.  
  1507.  
  1508. # ...................................................................
  1509. ConsiderTongue:
  1510. //    DEBUGPRINT("ConsiderTongue:");
  1511.     if (!b_OKToShowTongue)
  1512.     {
  1513. //        DEBUGPRINT("ConsiderTongue: Done");
  1514.         return;
  1515.     }
  1516.     // Don't show tongue while launching little snake
  1517.     if (!b_OKToLaunchLittleSnake)
  1518.     {
  1519. //        DEBUGPRINT("ConsiderTongue: Done");
  1520.         return;
  1521.     }
  1522.  
  1523. //    DEBUGPRINT("ConsiderTongue: SetToIndy");
  1524.     call SetToIndy;
  1525. //    DEBUGPRINT("ConsiderTongue: SetIndyOnLedge");
  1526.     call SetIndyOnLedge;
  1527.  
  1528.     // If not invisible
  1529. //    DEBUGPRINT("ConsiderTongue: Check Invisible");
  1530.     if (!BITTEST(GetActorFlags(t_Indy), 0x80))    
  1531.     {
  1532.         // Must be generally facing indy
  1533. //        DEBUGPRINT("ConsiderTongue: Check Facing");
  1534.         v_BossLook = GetThingLVec(t_Boss);
  1535.         f_Dot = VectorDot(v_BossLook, v_ToIndyFlat);
  1536.         if (f_Dot < -0.3)
  1537.         {
  1538. //            DEBUGPRINT("ConsiderTongue: Done");
  1539.             return;
  1540.         }
  1541.     
  1542.         // Don't be too close, we might attack
  1543.         // but if Indy is on a ledge then distance doesn't matter
  1544.         if (!b_IndyOnLedge)
  1545.         {
  1546.             if (f_DistToIndy < 1.0)
  1547.             {
  1548. //                DEBUGPRINT("ConsiderTongue: Done");
  1549.                 return;
  1550.             }
  1551.         }
  1552.     
  1553.         // Just sometimes even when can
  1554. //        DEBUGPRINT("ConsiderTongue: Random");
  1555.         if (RandBetween(1,100) > 60)
  1556.         {
  1557. //            DEBUGPRINT("ConsiderTongue: Done");
  1558.             return;
  1559.         }
  1560.     }
  1561.     // Indy is invisible
  1562.     else
  1563.     {
  1564.         if (RandBetween(1,100) > 30)
  1565.         {
  1566. //            DEBUGPRINT("ConsiderTongue: Done");
  1567.             return;
  1568.         }
  1569.     }
  1570.  
  1571. //    DEBUGPRINT("ConsiderTongue: HasLos");
  1572.     if (HasLos(t_Boss, t_Indy))
  1573.     {
  1574. //        DebugPrint("Show Tongue");
  1575.         b_OKToShowTongue = 0; 
  1576.         b_OKToLaunchLittleSnakeTemp = b_OKToLaunchLittleSnake;
  1577.         b_OKToLaunchLittleSnake = 0; // don't launch while playing
  1578. //        DEBUGPRINT("ConsiderTongue: PlayKey");
  1579.         PlaySoundThing(tonguehiss, t_Boss, 1.0, 0.0, 60.0, 0x880);
  1580.         PlayKey(t_Boss, key, 5, 0x1A, 1);
  1581. //        DEBUGPRINT("ConsiderTongue: PlayKey done");
  1582.         b_OKToLaunchLittleSnake = b_OKToLaunchLittleSnakeTemp;
  1583.         SetTimerEx(TONGUE_RELOAD, TIMER_ID_ENABLE_TONGUE, 0, 0);
  1584. //        DebugPrint("Done Show Tongue");
  1585.     }
  1586.  
  1587. //    DEBUGPRINT("ConsiderTongue: Done");
  1588.  
  1589.     return;
  1590.  
  1591.  
  1592. #==========================Don's Added Stuff===========================
  1593. killed:
  1594.     if(GetSenderRef() == t_boss)
  1595.     {
  1596.         DestroyThing(t_Boss);
  1597.         SendMessage(hints, user5);    # hint solved.
  1598.  
  1599.         #---------- music cue -------------
  1600.         PlaySoundLocal(music0, 1.0, 0.0, 0x0, 0);
  1601.  
  1602.         player = GetLocalPlayerThing();
  1603.         dustpos = CreateThingAtPos(dustghost, GetSurfaceSector(wall0), GetSurfaceCenter(wall0), VectorSet(0, 0, 0));
  1604.         CaptureThing(dustpos);
  1605.         sleep(2.0);
  1606.         curcam = GetCurrentCamera();
  1607.         MakeMeStop();
  1608.         DeselectWeaponWait(player);
  1609.         StartCutscene(1);
  1610.         SetCameraFocus(2, deathcampos);
  1611.         SetCameraSecondaryFocus(2, dustpos);
  1612.         SetCurrentCamera(2);
  1613.         SetCameraFOV(60, 1, 2);
  1614. #        setPulse(0.01);    
  1615.         PlaySoundThing(wallexpl, dustpos, 1, 15, 40, 0);
  1616.         for(i=0; i<pieces; i=i+1)                                                                        
  1617.         {
  1618.             fragment = CreateThing(debris0[RandBetween(0, 4)], dustpos);                                 
  1619.               SetThingVel(fragment, VectorScale(VectorAdd(RandVec(), '-0.5 -0.5 0.0'), 1.5));
  1620.               SetThingRotVel(fragment, VectorScale(VectorAdd(RandVec(), '0.0 0.0 0.0'), 200.0));
  1621.             sleep(0.025);
  1622.         }
  1623.         SetAdjoinFlags(wall0, 2);
  1624.         SetFaceGeoMode(wall0, 0);
  1625.         SetAdjoinFlags(wall1, 2);
  1626.         SetFaceGeoMode(wall1, 0);
  1627.         dust=CreateThing(dusttemp,dustpos);
  1628.         MaterialAnim(dustmat, 8.0, 0);
  1629.         AnimateSpriteSize(dust, '0.02 0.02 1.0', '0.4 0.4 0.5', 1.0);
  1630.         sleep(1.0);
  1631. #        setPulse(0.0);
  1632.         Destroything(dust);
  1633.         sleep(1.0);                                                                                                   
  1634.         SetCameraFOV(90, 0, 0);
  1635.         SetCurrentCamera(curcam);
  1636.         ClearActorFlags(player, 0x200000);
  1637.         EndCutscene();
  1638.     }
  1639.     else
  1640.     {     
  1641.         // check for one of the little snakes
  1642.         for (n_idx=0; n_idx < MAX_LITTLE_SNAKES; n_idx=n_idx + 1)
  1643.         {
  1644.             if (t_LittleSnake00[n_idx] == GetSenderRef())
  1645.             {
  1646.                 t_LittleSnake00[n_idx] = -1;
  1647.                 n_LittleSnakesAlive = n_LittleSnakesAlive - 1;
  1648.             }
  1649.         }
  1650.     }
  1651.     return;
  1652.  
  1653. user1:
  1654.     if(getParam(0) == 1)
  1655.     {
  1656.         AIClearCutsceneMode(t_Boss);
  1657.         AIClearMode(t_Boss, 0x1000); // turn off sleeping bit
  1658.         call PatrolMode;
  1659.         n_MoveSoundChannel = PlaySoundThing(move, t_Boss, 1.0, 0.0, 70.0, 0x881);
  1660. //        ChangeSoundPitch(n_MoveSoundChannel, 0.70, 0.1);
  1661.         return;
  1662.     }
  1663.     if(getParam(0) == 2)
  1664.     {
  1665.         PlaySoundThing(tonguehiss, t_Boss, 1.0, 0.0, 60.0, 0x880);
  1666.         PlayKey(t_Boss, key, 5, 0x1A, 0);
  1667.     }
  1668.     return;
  1669.  
  1670. end
  1671.  
  1672.